﻿//Competition.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Algo.History.Rts
{
	using System;
	using System.Collections.Generic;
	using System.Globalization;
	using System.Linq;
	using System.Text;
	using System.Web;

	using Ecng.Collections;
	using Ecng.Common;

	using HtmlAgilityPack;

	using StockSharp.BusinessEntities;

	/// <summary>
	/// Класс для получения данных к итогам конкурса ЛЧИ.
	/// </summary>
	public class Competition : BaseHistorySource
	{
		/// <summary>
		/// Итог конкурса ЛЧИ за определенный год.
		/// </summary>
		public class CompetitionYear
		{
			private readonly Competition _competition;

			internal CompetitionYear(Competition competition, DateTime year)
			{
				_competition = competition;
				Year = year;
			}

			/// <summary>
			/// Год проведения ЛЧИ.
			/// </summary>
			public DateTime Year { get; private set; }

			private string[] _members;

			/// <summary>
			/// Участники соревнования.
			/// </summary>
			public IEnumerable<string> Members
			{
				get
				{
					if (_members == null)
						Init();

					return _members;
				}
			}

			private DateTime[] _days;

			/// <summary>
			/// Даты, для которых есть информация о сделках.
			/// </summary>
			public IEnumerable<DateTime> Days
			{
				get
				{
					if (_days == null)
						Init();

					return _days;
				}
			}

			/// <summary>
			/// Получить сделки участника за определенную дату.
			/// </summary>
			/// <param name="member">Участник.</param>
			/// <param name="date">Дата соревнования.</param>
			/// <returns>Сделки участника.</returns>
			public IEnumerable<OrderLogItem> GetTrades(string member, DateTime date)
			{
				if (member.IsEmpty())
					throw new ArgumentNullException("member");

				if (!Days.Contains(date))
					throw new ArgumentOutOfRangeException("date", date, "Неизвестный день конкурса.");

				var doc = GetWeb().Load("http://investor.rts.ru/ru/statistics/{0}/?act=deals&nick={1}&date={2:yyyyMMdd}".Put(Year.Year, member, date));

				var portfolio = _competition._portfolios.SafeAdd(member, key => new Portfolio { Name = member });

				return CultureInfo.InvariantCulture.DoInCulture(() =>
					doc
						.DocumentNode
						.SelectNodes("//table[@class='tablels']")
						.Last()
						.Descendants("tr")
						.Skip(1)
						.Select(tr =>
						{
							var tds = tr.Descendants("td").Skip(date.Year > 2008 ? 1 : 0).ToArray();

							var securityId = tds[0].Descendants(date.Year == 2006 ? "b" : "a").First().InnerText;

							securityId = HttpUtility.HtmlDecode(securityId).ReplaceWhiteSpaces().Trim();

							var security = _competition.SecurityStorage.LoadBy("Id", securityId);

							if (security == null)
								throw new InvalidOperationException("Нет информация об инструменте {0}.".Put(securityId));

							var trade = new Trade
							{
								Security = security,
								Price = HttpUtility.HtmlDecode(tds[2].InnerText).ReplaceWhiteSpaces().Replace(" ", string.Empty).Replace(',', '.').To<decimal>(),
								Volume = HttpUtility.HtmlDecode(tds[3].InnerText).ReplaceWhiteSpaces().Replace(" ", string.Empty).To<decimal>(),
								Time = date + TimeSpan.ParseExact(HttpUtility.HtmlDecode(tds[4].InnerText).ReplaceWhiteSpaces().Trim(), "c", null)
							};

							var direction = (trade.Volume > 0) ? OrderDirections.Buy : OrderDirections.Sell;
							trade.Volume = Math.Abs(trade.Volume);

							return new OrderLogItem
							{
								Order = new Order
								{
									Portfolio = portfolio,
									Price = trade.Price,
									Security = trade.Security,
									Direction = direction,
									Volume = trade.Volume,
									Time = trade.Time,
									State = OrderStates.Done,
								},
								Trade = trade,
							};
						})
						.ToArray());
			}

			private void Init()
			{
				var doc = GetWeb().Load("http://investor.rts.ru/ru/statistics/{0}/default.aspx?act=deals".Put(Year.Year));
				_members = doc.DocumentNode.SelectSingleNode("//select[@name='nick']").Descendants("option").Select(n => n.Attributes["value"].Value).ToArray();
				_days = doc.DocumentNode.SelectSingleNode("//select[@name='date']").Descendants("option").Select(n => n.Attributes["value"].Value.ToDateTime("yyyyMMdd")).OrderBy().ToArray();
			}

			private static HtmlWeb GetWeb()
			{
				return new HtmlWeb { OverrideEncoding = Encoding.GetEncoding(1251) };
			}
		}

		private readonly SynchronizedDictionary<string, Portfolio> _portfolios = new SynchronizedDictionary<string, Portfolio>(); 
		private readonly SynchronizedDictionary<int, CompetitionYear> _competitions = new SynchronizedDictionary<int, CompetitionYear>();

		static Competition()
		{
			_allYears = new DateTime[7];

			for (var i = 6; i <= 12; i++)
				_allYears[i - 6] = new DateTime(2000 + i, 1, 1);
		}

		/// <summary>
		/// Создать <see cref="Competition"/>.
		/// </summary>
		/// <param name="securityStorage">Интерфейс для доступа к хранилищу информации об инструментах.</param>
		public Competition(ISecurityStorage securityStorage)
			: base(securityStorage)
		{
		}

		private static readonly DateTime[] _allYears;

		/// <summary>
		/// Все года, когда проводился конкурс.
		/// </summary>
		public static IEnumerable<DateTime> AllYears
		{
			get { return _allYears; }
		}

		/// <summary>
		/// Получить итоги за определенный год.
		/// </summary>
		/// <param name="year">Год конкурса.</param>
		/// <returns>Итоги.</returns>
		public CompetitionYear Get(DateTime year)
		{
			return _competitions.SafeAdd(year.Year, key => new CompetitionYear(this, year));
		}
	}
}